/**
 * Xilinx PS-GPIO 驱动适配层
 * 张勇 / 41204@qq.com
 */
#include <xparameters.h>

// 如果未使用 PS-GPIO 则不实现
#ifdef XPAR_XGPIOPS_NUM_INSTANCES

#include <stdbool.h>
#include <xgpiops.h>
#include "mcu.h"

/// PS-GPIO Driver 实例
static XGpioPs  _gpiops_inst[XPAR_XGPIOPS_NUM_INSTANCES];
static XGpioPs* _gpiops[XPAR_XGPIOPS_NUM_INSTANCES] = { NULL };

int scugic_intr_type(mcu_gpio_intr_type_t trig) {
	/**/ if(trig == MCU_GPIO_INTR_POSEDGE) return 3; // 0x3：上升沿触发
	else if(trig == MCU_GPIO_INTR_NEGEDGE) return 2; // 0x2：下降沿触发
	else if(trig == MCU_GPIO_INTR_LVHIGH)  return 1; // 0x1：高电平触发
	else if(trig == MCU_GPIO_INTR_LVLOW)   return 0; // 0x0：低电平触发
	else return -1;
}

int scugic_intr_type_toggle_polarity(uint8_t irq_type) {
	/**/ if(irq_type == 3) return 2;
	else if(irq_type == 2) return 3;
	else if(irq_type == 1) return 0;
	else if(irq_type == 0) return 1;
	else return -1;
}

int gpiops_intr_type(mcu_gpio_intr_type_t trig) {
	/**/ if(trig == MCU_GPIO_INTR_POSEDGE) return XGPIOPS_IRQ_TYPE_EDGE_RISING;
	else if(trig == MCU_GPIO_INTR_NEGEDGE) return XGPIOPS_IRQ_TYPE_EDGE_FALLING;
	else if(trig == MCU_GPIO_INTR_2EDGES)  return XGPIOPS_IRQ_TYPE_EDGE_BOTH;
	else if(trig == MCU_GPIO_INTR_LVHIGH)  return XGPIOPS_IRQ_TYPE_LEVEL_HIGH;
	else if(trig == MCU_GPIO_INTR_LVLOW)   return XGPIOPS_IRQ_TYPE_LEVEL_LOW;
	else return -1;
}

int gpiops_intr_type_toggle_polarity(uint8_t irq_type) {
	/**/ if(irq_type == XGPIOPS_IRQ_TYPE_EDGE_RISING)  return XGPIOPS_IRQ_TYPE_EDGE_FALLING;
	else if(irq_type == XGPIOPS_IRQ_TYPE_EDGE_FALLING) return XGPIOPS_IRQ_TYPE_EDGE_RISING;
	else if(irq_type == XGPIOPS_IRQ_TYPE_LEVEL_LOW)    return XGPIOPS_IRQ_TYPE_LEVEL_HIGH;
	else if(irq_type == XGPIOPS_IRQ_TYPE_LEVEL_HIGH)   return XGPIOPS_IRQ_TYPE_LEVEL_LOW;
	else return -1;
}

std_err_t GPIO_Config(uint16_t devid, uint16_t pin, mcu_gpio_mode_t mode) {
	if(devid >= XPAR_XGPIOPS_NUM_INSTANCES || mode >= MCU_GPIO_MODE_MAX) return STD_ERR_INVALID_ARG;

	// 如果 GPIO Driver 实例未初始化，则先初始化一次
	if(!_gpiops[devid]) {
		XGpioPs_Config* cfg = XGpioPs_LookupConfig(devid);                             if(cfg == NULL) return STD_ERR_FAIL;
		s32 xStatus = XGpioPs_CfgInitialize(&_gpiops_inst[devid], cfg, cfg->BaseAddr); if(XST_SUCCESS != xStatus) return STD_ERR_FAIL;
		_gpiops[devid] = &_gpiops_inst[devid];
	}

	if(mode == MCU_GPIO_MODE_NONE) {
		XGpioPs_SetOutputEnablePin(_gpiops[devid], pin, false);// 关闭输出
	} else if(mode == MCU_GPIO_MODE_INPUT) {
		XGpioPs_SetDirectionPin(_gpiops[devid], pin, 0);
		XGpioPs_SetOutputEnablePin(_gpiops[devid], pin, false);// 关闭输出
	} else if(mode == MCU_GPIO_MODE_OUTPUT) {
		XGpioPs_SetDirectionPin(_gpiops[devid], pin, 1);
		XGpioPs_SetOutputEnablePin(_gpiops[devid], pin, true); // 使能输出
	} else {
		return STD_ERR_INVALID_ARG;
	}
	return STD_ERR_OK;
}

std_err_t GPIO_SetLevel(uint16_t devid, uint16_t pin, uint8_t level) {
	XGpioPs_WritePin(_gpiops[devid], pin, level);
	return STD_ERR_OK;
}

uint8_t GPIO_GetLevel(uint16_t devid, uint16_t pin) {
	return (uint8_t)XGpioPs_ReadPin(_gpiops[devid], pin);
}

uint8_t GPIO_BidiIn(uint16_t devid, uint16_t pin) {
	// 如果当前配置不是 IN 模式，则先配置为 IN 模式
	uint32_t dir = XGpioPs_GetDirectionPin(_gpiops[devid], pin);
	if(dir != 0) XGpioPs_SetDirectionPin(_gpiops[devid], pin, 0);
	return (uint8_t)XGpioPs_ReadPin(_gpiops[devid], pin);
}

std_err_t GPIO_BidiOut(uint16_t devid, uint16_t pin, uint8_t level) {
	// 如果当前配置不是 OUT 模式，则先配置为 OUT 模式
	uint32_t dir = XGpioPs_GetDirectionPin(_gpiops[devid], pin);
	if(dir != 1) XGpioPs_SetDirectionPin(_gpiops[devid], pin, 1);
	XGpioPs_WritePin(_gpiops[devid], pin, level);
	return STD_ERR_OK;
}

std_err_t GPIO_EXTI_Config(uint16_t devid, uint16_t pin, uint32_t irqn, uint8_t priority, mcu_gpio_intr_type_t trig, mcu_isr_t isr, void *cookie) {
	if(trig >= MCU_GPIO_INTR_MAX || !isr) return STD_ERR_INVALID_ARG;
	int gpio_irq_type = gpiops_intr_type(trig); if(gpio_irq_type < 0) return STD_ERR_INVALID_ARG;
	int gic_irq_type  = scugic_intr_type(trig); if(gic_irq_type  < 0) return STD_ERR_INVALID_ARG;

	// 设置 GPIO 中断触发类型
	XGpioPs_SetIntrTypePin(_gpiops[devid], pin, gpio_irq_type);
	// 设置 GIC 中断触发类型
	mcu_intc_set_priority_trigtype(mcu_intc(), irqn, priority, gic_irq_type);
	// 为中断设置中断处理函数
	return mcu_intc_register_handler(mcu_intc(), irqn, isr, cookie);
}

std_err_t GPIO_EXTI_Enable(uint16_t devid, uint16_t pin, uint32_t irqn, bool enable) {
	if(enable) {
		XGpioPs_IntrEnablePin(_gpiops[devid], pin);     // 使能 PS-GPIO 引脚的中断
		return mcu_intc_enable(mcu_intc(), irqn, enable);            // 使能 GIC 对应 IRQN 的中断
	} else {
		//mcu_irq_enable(irqn, enable);                 // 禁用 GIC 对应 IRQN 的中断 ?
		XGpioPs_IntrDisablePin(_gpiops[devid], pin);    // 禁用 PS-GPIO 引脚的中断
		return STD_ERR_OK;
	}
}

std_err_t GPIO_EXTI_ToggleTrigPolarity(uint16_t devid, uint16_t pin, uint32_t irqn) {
	uint8_t priority, irq_type;
	int irq_type2;

	irq_type = XGpioPs_GetIntrTypePin(_gpiops[devid], pin);
	irq_type2 = gpiops_intr_type_toggle_polarity(irq_type); if(irq_type2 < 0) return STD_ERR_INVALID_STATE;
	XGpioPs_SetIntrTypePin(_gpiops[devid], pin, irq_type2);
	
	mcu_intc_get_priority_trigtype(mcu_intc(), irqn, &priority, &irq_type);
	irq_type2 = scugic_intr_type_toggle_polarity(irq_type); if(irq_type2 < 0) return STD_ERR_INVALID_STATE;
	mcu_intc_set_priority_trigtype(mcu_intc(), irqn,  priority,  irq_type2);
	return STD_ERR_OK;
}

bool GPIO_EXTI_Flag(uint16_t devid, uint16_t pin) {
	return XGpioPs_IntrGetStatusPin(_gpiops[devid], pin);
}

std_err_t GPIO_EXTI_Clear(uint16_t devid, uint16_t pin) {
	XGpioPs_IntrClearPin(_gpiops[devid], pin);
	return STD_ERR_OK;
}

#endif // XPAR_XGPIOPS_NUM_INSTANCES
